/*
 * Copyright 2004 Program of Computer Graphics, Cornell University
 *     580 Rhodes Hall
 *     Cornell University
 *     Ithaca NY 14853
 * Web: http://www.graphics.cornell.edu/
 * 
 * Not for commercial use. Do not redistribute without permission.
 */

#ifndef _H_SPHERE
#define _H_SPHERE

#include "Geometry.h"
#include <cstdio>

//A sphere is a hollow shell of a 3D sphere (x^2 + y^2 + z^2 = radius^2)
class Sphere : public GeometryObject {
private:

// Padding data for 16 byte alignment in 64 bits. Because of the size
// of the "this" pointer, the size of the class would have growth to
// 64 bytes anyway
#ifdef BRITE_64
  BRITE_UNUSED float dummy1,dummy2,dummy3;
#endif

  float uX,uY,uZ;   //u-axis (sphere local coordinate frame)
  float centerX,centerY,centerZ;
  float radius;
  float wX,wY,wZ;   //w-axis (sphere local coordinate frame)
  int mungedId;
  //note v-axis is w-axis cross u-axis

public:
  Sphere() {}
  ~Sphere() {
    BRITE_DEBUGBREAK;  //geometry objects are now pool allocated and so their destructor should never be called
  }
  void set(float _centerX, float _centerY, float _centerZ, 
    float _uX, float _uY, float _uZ,
    float _wX, float _wY, float _wZ,
    float _radius, int _id) {
    if (_id&~GID_VALUE_MASK) { 
	  printf("bad geometry(p) id %d\n",_id);
      BRITE_DEBUGBREAK;
    }
    centerX = _centerX; centerY = _centerY; centerZ = _centerZ;
    uX = _uX; uY = _uY; uZ = _uZ;
    wX = _wX; wY = _wY; wZ = _wZ;
    radius = _radius;
    mungedId = _id | GID_FLAG_FP;
  }
  static Sphere *poolAlloc(PoolAllocator *pool) {
    void *ptr = pool->allocate(sizeof(Sphere),SIMD_SIZE);
    return new(ptr) Sphere();
  }
  virtual int getType() const { return GEOM_SPHERE; }
  virtual PrimitiveList *compile(int num,const GeometryObject **array,PoolAllocator *pool)const;
  virtual bool intersectsBox(const AxisAlignedBox &box)const;
  virtual void expandBoxToInclude(AxisAlignedBox *box)const;
  //estimate the bounding box of the portion of the object inside the specified bounding box
  virtual AxisAlignedBox estimateIntersectionBounds(const AxisAlignedBox &box)const;
  //return approximate cost relative to a triangle
  virtual int estimateRelativeCost() const { return 2; }

  int id() const { return mungedId&GID_VALUE_MASK; }
  //get center as SIMD vectors of 4 floats (value of fourth float is not specified)
  F32vec4 getUAxis() const {
#ifndef BRITE_64
    F32vec4 temp = Load_F32vec4((float *)this);
#else
    F32vec4 temp = Load_F32vec4(&this->dummy3);
#endif
    return _mm_shuffle_ps(temp,temp,_MM_SHUFFLE(3,3,2,1));
  }
  F32vec4 getWAxis() const { return Load_F32vec4(&this->wX); }
  F32vec4 getCenter() const { return Load_F32vec4(&this->centerX); }
  AxisAlignedBox getBoundingBox() const;
  bool intersectsRay(const Ray &ray, IntersectionRecord *hitRec) const;
};


//types used when we compile geometry into PrimitiveLists for fast intersection testing
//disks are not packed very tightly at the moment because we assume that they are not
//very common.  Packing could be improved in the future
class ALIGN16 PackedSphere {
public:
  F32vec4 center;
  F32vec4 uAxis;
  F32vec4 wAxis;
  float radius;
  int id;
};

//intersection routine for packed disks
void IntersectPackedSpheres(class PrimitiveList *, class Ray *ray, class IntersectionRecord *);

class PackedSphereList : public PrimitiveList {
public:
  PackedSphere spheres[1]; //actual number will be specified in length field

  static PackedSphereList* poolAlloc(int numSpheres, PoolAllocator *pool) {
    int size = sizeof(PrimitiveList) + (numSpheres)*sizeof(PackedSphere);
    PackedSphereList *list = (PackedSphereList*)pool->allocate(size);
    memset(list,0,size);
    list->length = numSpheres;
    list->setType(GEOM_PACKED_SPHERE);
    list->func = IntersectPackedSpheres;
    return list;
  }
  void setSphere(int index, float centerX, float centerY, float centerZ, 
                  float uAxisX, float uAxisY, float uAxisZ,
                  float wAxisX, float wAxisY, float wAxisZ,
                  float radius, int id);
};



#endif //_H_SPHERE

